#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "ObjArray.h"

#include "ContainerTypes.h"
#include "Class.h"
#include "Error.h"
#include "Math.h"
#include "ET_stdlib.h"

const char
	*cOutOfBoundsError = "index %d out of bounds (size: %d, this: 0x%08x)",
	*cMethodName       = "operator[]";

//---- ObjArray ----------------------------------------------------------------

NewMetaImpl(ObjArray,SeqCollection, (T(lb), TVP(cont,size)));

ObjArray::ObjArray(int s, int lowerBound)
{
	if (s <= 0)
		s= 10;
	cont= new Object*[size= s];
	lb= lowerBound;
}

ObjArray::~ObjArray()
{
	SafeDelete(cont);
}

void ObjArray::InitNew()
{
	cont= new Object*[cContainerInitCap];
	lb= 0;
}

void ObjArray::FreeAll()
{
	register int i;

	for (i= 0; i < size; i++) {
		if (cont[i]) {
			cont[i]->FreeAll();
			SafeDelete(cont[i]);
		}
	}
	size= 0;
}

Object *ObjArray::RemoveAt(int i)
{
	if (!BoundsOk("RemoveAt", i))
		return 0;
	Object *t= cont[i-lb];
	cont[i-lb]= 0;
	Changed();
	return t;
}

Object *ObjArray::Remove(Object *a)
{
	register int i;
	register Object *op;

	for (i= 0; i < size; i++) {
		op= cont[i];
		if (op && op->IsEqual(a)) {
			cont[i]= 0;
			Changed();
			return op;
		}
	}
	return 0;
}

Object *ObjArray::RemovePtr(Object *a)
{
	register int i;
	register Object *op;

	for (i= 0; i < size; i++) {
		op= cont[i];
		if (op && op == a) {
			cont[i]= 0;
			Changed();
			return op;
		}
	}
	return 0;
}

void ObjArray::Expand(int newSize)
{
	if (newSize < 0) {
		Error ("Expand", "newSize < 0");
		return;
	}
	if (newSize == size)
		return;
	if (newSize <= size) {
		// if the array is shrinked check whether there are nonempty entries
		for (int j= newSize; j < size; j++)
			if (cont[j]) {
				Error ("Expand", "expand would cut off nonempty entries at %d", j);
				return;
			}
	}
	cont= (Object**) Storage::ReAlloc(cont, newSize * sizeof(Object*));
	if (newSize > size) // clear slots
		for (int i= size; i < newSize; i++)
			cont[i]= 0;
	size= newSize;
}

Iterator *ObjArray::MakeIterator(bool dir, void *placement)
{
	if (dir == cIterForward)
		return new(placement) ObjArrayIter(this);
	Error("MakeIterator", "no reversed iterator");  // ?? why that
	return 0;
}

Object *ObjArray::AtPut(int i, Object *ob)
{
	if (!BoundsOk("AtPut", i))
		return 0;
	Object *t= cont[i-lb];
	cont[i-lb]= ob;
	Changed();
	return t;
}

void ObjArray::AtPutAndExpand(int i,Object *op)
{
	if (i < lb) {
		Error ("AtPutAndExpand", "out of bounds at %d in %x", i, this);
		return;
	}
	if (i-lb >= size)
		Expand (Math::Max(i, GrowBy(size)));
	cont[i-lb]= op;
	Changed();
}

Object *ObjArray::Add(Object *op)
{
	register int i;

	if (cont == 0)
		cont= new Object*[size= 10];
	for (i= 0; i < size; i++)
		if (cont[i] == 0)
			break;
	if (i >= size)
		Expand(GrowBy(size));
	cont[i]= op;
	Changed();
	return 0;
}

int ObjArray::Index(Object *op)
{
	register int i, fi= -1;

	if (cont == 0)
		cont= new Object*[size= 10];
	for (i= 0; i < size; i++) {
		if (cont[i] == op)
			return i+lb;
		if (cont[i] == 0 && fi == -1)
			fi= i;
	}
	if (fi == -1) {
		Expand(GrowBy(size));
		fi= i;
	}
	cont[fi]= op;
	return fi+lb;
}

int ObjArray::IndexOf(Object *ob)
{
	register int i;

	for (i= 0; i < size; i++)
		if (ob && ob->IsEqual(cont[i]))
			return i+lb;
	return -1;
}

int ObjArray::IndexOfPtr(Object *ob)
{
	register int i;

	for (i= 0; i < size; i++)
		if (ob == cont[i])
			return i+lb;
	return -1;
}

Object *ObjArray::At(int i)
{
	if (!BoundsOk("At", i))
		return 0;
	return cont[i-lb];
}

int ObjArray::Compare(Object*)
{
	MayNotUse ("Compare");
	return -2;
}

bool ObjArray::IsEqual(Object *ob)
{
	if (!ob->IsKindOf(ObjArray))
		return FALSE;
	ObjArray *t= (ObjArray*) ob;
	if (t->size != size || t->lb != lb)
		return FALSE;
	for (int i= 0; i < Size(); i++) {
		if (cont[i] == 0 && t->cont[i] == 0)
			continue;
		if (cont[i] == 0 || t->cont[i] == 0)
			break;
		if (!cont[i]->IsEqual(t->cont[i]))
			break;
	}
	return i == size;
}

OStream& ObjArray::PrintOn(OStream &s)
{
	Object::PrintOn(s); // supress Collection::PrintOn
	s << Size() SP;
	for (int i= 0; i < Size(); i++)
		s << cont[i] SP;
	return s NL;
}

IStream& ObjArray::ReadFrom(IStream &s)
{
	Object::ReadFrom(s);
	delete cont;
	s >> size;
	cont= new Object*[size];
	for (int i= 0; i < size; i++)
		s >> cont[i];
	return s;
}

unsigned long ObjArray::Hash ()
{
	register u_long hash= 0;

	for (int i= 0; i < size; i++)
		if (cont[i])
			hash ^= cont[i]->Hash();
	return hash;
}

void ObjArray::Sort(int upto)
{
	::qsort(cont, Math::Min(size, upto-lb), sizeof(Object*), &::ppCompare);
}

int ObjArray::BinarySearch(Object *op, int upto)
{
	register int base, position, last, result;
	register Object *op2;

	if (op == 0)
		return -1;

	base= 0;
	last= Math::Min(size, upto-lb) - 1;

	while (last >= base) {
		position= (base+last) / 2;
		op2= cont[position];
		if ((op2 == 0) || (result= op->Compare(op2)) == 0)
			return position + lb;
		if (result < 0)
			last= position-1;
		else
			base= position+1;
	}
	return -1;
}

//---- ObjArrayIter ------------------------------------------------------------

ObjArrayIter::ObjArrayIter(ObjArray *oa)
{
	cs= oa; ce= 0;
}

ObjArrayIter::~ObjArrayIter()
{
}

void ObjArrayIter::Reset()
{
	ce= 0;
}

Object *ObjArrayIter::operator()()
{
	for (;ce < cs->Size() && cs->cont[ce] == 0; ce++)
		;
	if (ce < cs->Size())
		return cs->cont[ce++];
	return 0;
}

bool ObjArrayIter::Filter(Object*)
{
	return TRUE;
}

